/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.type;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.apache.ibatis.io.ResolverUtil;
import org.apache.ibatis.io.Resources;

/**
 * 类型别名注册器
 * 用于取代复杂的类型全限定名，用于映射器配置文件中进行参数类型与返回结果类型的设置，
 * MyBatis会在进行数据库操作之前进行参数类型别名的解析操作获取具体的参数类型，
 * 又会在数据库操作之后进行结果类型别名的解析获取具体的结果类型，
 * 再通过类型处理器进行类型处理来将参数和结果分别进行匹配映射。
 * @author Clinton Begin
 */
public class TypeAliasRegistry {

  /**
   * 别名注册器实际存储的别名和实际类型映射关系的地方，通过HashMap键值对实现。
   */
  private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();

  /**
   * 构造器,大量基础类型别名的注册
   * 注意：这并不是全部的MyBatis内置的类型别名，还有一部分类型别名是在创建Configuration实例的时候在其无参构造器中进行注册的
   */
  public TypeAliasRegistry() {
	//字符串类型
    registerAlias("string", String.class);
    //基本包装类型
    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
    registerAlias("int", Integer.class);
    registerAlias("integer", Integer.class);
    registerAlias("double", Double.class);
    registerAlias("float", Float.class);
    registerAlias("boolean", Boolean.class);
    //基本数组包装类型
    registerAlias("byte[]", Byte[].class);
    registerAlias("long[]", Long[].class);
    registerAlias("short[]", Short[].class);
    registerAlias("int[]", Integer[].class);
    registerAlias("integer[]", Integer[].class);
    registerAlias("double[]", Double[].class);
    registerAlias("float[]", Float[].class);
    registerAlias("boolean[]", Boolean[].class);
    //加个下划线，就变成了基本类型
    registerAlias("_byte", byte.class);
    registerAlias("_long", long.class);
    registerAlias("_short", short.class);
    registerAlias("_int", int.class);
    registerAlias("_integer", int.class);
    registerAlias("_double", double.class);
    registerAlias("_float", float.class);
    registerAlias("_boolean", boolean.class);
    //加个下划线，就变成了基本数组类型
    registerAlias("_byte[]", byte[].class);
    registerAlias("_long[]", long[].class);
    registerAlias("_short[]", short[].class);
    registerAlias("_int[]", int[].class);
    registerAlias("_integer[]", int[].class);
    registerAlias("_double[]", double[].class);
    registerAlias("_float[]", float[].class);
    registerAlias("_boolean[]", boolean[].class);
    //日期数字型
    registerAlias("date", Date.class);
    registerAlias("decimal", BigDecimal.class);
    registerAlias("bigdecimal", BigDecimal.class);
    registerAlias("biginteger", BigInteger.class);
    registerAlias("object", Object.class);
    //日期数字型 数组
    registerAlias("date[]", Date[].class);
    registerAlias("decimal[]", BigDecimal[].class);
    registerAlias("bigdecimal[]", BigDecimal[].class);
    registerAlias("biginteger[]", BigInteger[].class);
    registerAlias("object[]", Object[].class);
    //集合型
    registerAlias("map", Map.class);
    registerAlias("hashmap", HashMap.class);
    registerAlias("list", List.class);
    registerAlias("arraylist", ArrayList.class);
    registerAlias("collection", Collection.class);
    registerAlias("iterator", Iterator.class);
    //还有个ResultSet型
    registerAlias("ResultSet", ResultSet.class);
  }

  /**
   * 解析别名，通过别名获取集合中保存的别名对应的值，即类类型。
   * @param string
   * @return
   */
  @SuppressWarnings("unchecked")
  // throws class cast exception as well if types cannot be assigned
  public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) {//别名如果为空，就直接返回null
        return null;
      }
      // issue #748
      String key = string.toLowerCase(Locale.ENGLISH);
      Class<T> value;
      if (TYPE_ALIASES.containsKey(key)) {//如果在别名中注册了，就直接获取别名对应的类类型
        value = (Class<T>) TYPE_ALIASES.get(key);
      } else {
    	////找不到，再试着将String直接转成Class(这样怪不得我们也可以直接用java.lang.Integer的方式定义，也可以就int这么定义)
        value = (Class<T>) Resources.classForName(string);
      }
      return value;
    } catch (ClassNotFoundException e) {
      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
  }

  /**
   * 包统一注册方式,对应的是如下的设置方式
   * <typeAliases>
   *  	<package name="com.xx.xx.xx"/>
   * </typeAliases>
   * @param packageName
   */
  public void registerAliases(String packageName){
    registerAliases(packageName, Object.class);
  }
  /**
   * 包统一注册方式,实现检索需要注册别名的类，并调用registerAlias(Class<?> type)实现注册
   * 表示注册指定包名下的所有类，它调用了第二个方法，其第二个参数目的是限定要注册的类的来源，只有继承自给定类型的类才能被注册，
   * 这里赋值为Object.class表示其下的所有类均在别名注册的考虑范围。
   * @param packageName
   * @param superType
   */
  public void registerAliases(String packageName, Class<?> superType){
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
    for(Class<?> type : typeSet){
      // Ignore inner classes and interfaces (including package-info.java)
      // Skip also inner classes. See issue #6
      //排除匿名类、接口、内部类等
      if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
    	  //注册别名
    	  registerAlias(type);
      }
    }
  }

  /**
   * 根据类的Alias注解值或者简名注册别名
   * @param type
   */
  public void registerAlias(Class<?> type) {
    String alias = type.getSimpleName();
    //判断需要注册别名的类上，是否有Alias注解，如果有就使用注解中的值，否则就用类的简名
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    if (aliasAnnotation != null) {
      alias = aliasAnnotation.value();
    } 
    //调用通用的别名注册方法实现类别名的注册
    registerAlias(alias, type);
  }

  /**
   * 注册类的别名的方法
   * 
   * @param alias
   * @param value
   */
  public void registerAlias(String alias, Class<?> value) {
    if (alias == null) {//别名不允许为空，否则抛出异常
      throw new TypeException("The parameter alias cannot be null");
    }
    // issue #748
    String key = alias.toLowerCase(Locale.ENGLISH);
    //如果已经存在key了，且value和之前不一致，报错
    if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
    }
    //把该别名和类型注册到HashMap对象中
    TYPE_ALIASES.put(key, value);
  }

  /**
   * 逐个注册方式,对应的是如下的设置方式
   * 	<typeAliases>
   *  		<typeAlias type="com.xxx.xx.Role" alias="role" />
   *	</typeAliases>
   *因为在配置中已经将目标与所要设置的别名名称都指定好了，因此只需要直接执行核心注册方法即可完成注册工作，
   *这种方式适合少量的类型注册情况，但是一旦需要注册的类型较多，工作就会显得很是复杂繁琐，
   *为了简化工作我们可以采用之前第一种方式，要采用这种方式就要在架构编码时有意的将需要进行类型别名注册的类放置到统一的包下。
   * @param alias
   * @param value
   */
  public void registerAlias(String alias, String value) {
    try {
      registerAlias(alias, Resources.classForName(value));
    } catch (ClassNotFoundException e) {
      throw new TypeException("Error registering type alias "+alias+" for "+value+". Cause: " + e, e);
    }
  }
  
  /**
   * 返回一个不可修改的TYPE_ALIASES视图
   * @since 3.2.2
   */
  public Map<String, Class<?>> getTypeAliases() {
	  //返回TYPE_ALIASES集合的不可修改视图。
    return Collections.unmodifiableMap(TYPE_ALIASES);
  }

}
